/*      */ package com.zimbra.cs.service.formatter;
/*      */ 
/*      */ import com.google.common.base.Objects;
/*      */ import com.google.common.base.Objects.ToStringHelper;
/*      */ import com.google.common.base.Strings;
/*      */ import com.zimbra.common.calendar.ICalTimeZone;
/*      */ import com.zimbra.common.localconfig.KnownKey;
/*      */ import com.zimbra.common.localconfig.LC;
/*      */ import com.zimbra.common.mailbox.ContactConstants;
/*      */ import com.zimbra.common.service.ServiceException;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.cs.mailbox.Contact;
/*      */ import com.zimbra.cs.mailbox.ContactGroup;
/*      */ import com.zimbra.cs.mailbox.MailItem;
/*      */ import com.zimbra.cs.mailbox.Mailbox;
/*      */ import com.zimbra.cs.mailbox.OperationContext;
/*      */ import java.io.BufferedReader;
/*      */ import java.io.FileInputStream;
/*      */ import java.io.IOException;
/*      */ import java.io.InputStream;
/*      */ import java.io.OutputStream;
/*      */ import java.io.PrintStream;
/*      */ import java.text.SimpleDateFormat;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Collections;
/*      */ import java.util.GregorianCalendar;
/*      */ import java.util.HashMap;
/*      */ import java.util.HashSet;
/*      */ import java.util.Iterator;
/*      */ import java.util.List;
/*      */ import java.util.Map;
/*      */ import java.util.Map.Entry;
/*      */ import java.util.Set;
/*      */ import java.util.TreeSet;
/*      */ import org.dom4j.Document;
/*      */ import org.dom4j.DocumentException;
/*      */ import org.dom4j.QName;
/*      */ import org.dom4j.io.SAXReader;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public final class ContactCSV
/*      */ {
/*   58 */   private static Log LOG = ZimbraLog.misc;
/*      */   
/*      */ 
/*      */   private static final char DEFAULT_FIELD_SEPARATOR = ',';
/*      */   
/*      */   private static final String NEW_LINE = "\r\n";
/*      */   
/*   65 */   private static final char[] SUPPORTED_SEPARATORS = { ',', ';' };
/*   66 */   private static enum ColType { SIMPLE,  MULTIVALUE,  NAME,  TAG,  DATE;
/*      */     
/*      */     private ColType() {} }
/*      */   
/*      */   private int lineNumber;
/*      */   private int currContactStartLineNum;
/*      */   private ArrayList<String> fieldNames;
/*      */   private boolean detectFieldSeparator;
/*      */   private boolean knowFieldSeparator;
/*   75 */   private char fieldSeparator; private Mailbox mbox = null;
/*   76 */   private OperationContext octxt = null;
/*      */   
/*      */   private static Set<String> knownFields;
/*      */   
/*      */   private static Set<CsvFormat> knownFormats;
/*      */   private static Map<String, Character> delimiterInfo;
/*      */   private static Map<String, String> dateOrderInfo;
/*      */   private static CsvFormat defaultFormat;
/*   84 */   private static final QName DATEFORMATS = QName.get("dateformats");
/*   85 */   private static final QName DATEFORMAT = QName.get("dateformat");
/*   86 */   private static final QName DELIMITERS = QName.get("delimiters");
/*   87 */   private static final QName DELIMITER = QName.get("delimiter");
/*   88 */   private static final QName FIELDS = QName.get("fields");
/*   89 */   private static final QName FIELD = QName.get("field");
/*   90 */   private static final QName FORMAT = QName.get("format");
/*   91 */   private static final QName COLUMN = QName.get("column");
/*      */   private static final String ATTR_CHAR = "char";
/*      */   private static final String ATTR_NAME = "name";
/*      */   private static final String ATTR_LOCALE = "locale";
/*      */   private static final String ATTR_FIELD = "field";
/*      */   private static final String ATTR_FLAG = "flag";
/*      */   private static final String ATTR_FORMAT = "format";
/*      */   private static final String ATTR_ORDER = "order";
/*      */   private static final String ATTR_TYPE = "type";
/*      */   private static final String TAG = "__tag";
/*      */   
/*      */   private ContactCSV() {
/*  103 */     this(',', true);
/*      */   }
/*      */   
/*      */   public ContactCSV(Mailbox mbox, OperationContext octxt) {
/*  107 */     this(',', true);
/*      */   }
/*      */   
/*      */ 
/*      */   private ContactCSV(char defaultFieldSeparator, boolean detectFieldSeparator)
/*      */   {
/*  113 */     this.fieldSeparator = defaultFieldSeparator;
/*  114 */     this.detectFieldSeparator = detectFieldSeparator;
/*      */     
/*  116 */     this.knowFieldSeparator = (!detectFieldSeparator);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private boolean isFieldSeparator(int testChar)
/*      */   {
/*  123 */     if (this.knowFieldSeparator) {
/*  124 */       return testChar == this.fieldSeparator;
/*      */     }
/*  126 */     for (char possSep : SUPPORTED_SEPARATORS) {
/*  127 */       if (possSep == testChar) {
/*  128 */         LOG.debug("CSV Separator character used='%c'", new Object[] { Character.valueOf(possSep) });
/*  129 */         this.knowFieldSeparator = true;
/*  130 */         this.fieldSeparator = possSep;
/*  131 */         return true;
/*      */       }
/*      */     }
/*  134 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private boolean parseLine(BufferedReader reader, List<String> result, boolean parsingHeader)
/*      */     throws IOException, ContactCSV.ParseException
/*      */   {
/*  142 */     this.currContactStartLineNum = this.lineNumber;
/*  143 */     if ((parsingHeader) && (this.detectFieldSeparator)) {
/*  144 */       this.knowFieldSeparator = false;
/*      */     }
/*  146 */     result.clear();
/*      */     
/*  148 */     boolean inField = false;
/*  149 */     int ch; while ((ch = reader.read()) != -1) {
/*  150 */       switch (ch) {
/*      */       case 34: 
/*  152 */         inField = true;
/*  153 */         result.add(parseField(reader, true, -1));
/*  154 */         break;
/*      */       case 10: 
/*  156 */         this.lineNumber += 1;
/*  157 */         if (!result.isEmpty())
/*      */         {
/*      */ 
/*  160 */           return true; }
/*      */         break;
/*      */       case 13: 
/*  163 */         this.lineNumber += 1;
/*      */         
/*  165 */         reader.mark(1);
/*  166 */         ch = reader.read();
/*  167 */         if (ch != 10) {
/*  168 */           reader.reset();
/*      */         }
/*  170 */         if (!result.isEmpty())
/*      */         {
/*      */ 
/*  173 */           return true; }
/*      */         break;
/*      */       default: 
/*  176 */         if (isFieldSeparator(ch)) {
/*  177 */           if (inField) {
/*  178 */             inField = false;
/*      */           } else {
/*  180 */             result.add(null);
/*      */           }
/*      */         }
/*      */         else {
/*  184 */           result.add(parseField(reader, false, ch));
/*  185 */           inField = false;
/*      */         }
/*      */         break;
/*      */       }
/*      */     }
/*  190 */     return !result.isEmpty();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private String parseField(BufferedReader reader, boolean doubleQuotes, int firstChar)
/*      */     throws IOException, ContactCSV.ParseException
/*      */   {
/*  204 */     StringBuilder result = new StringBuilder();
/*      */     
/*  206 */     if (firstChar != -1) {
/*  207 */       result.append((char)firstChar);
/*      */     }
/*      */     
/*  210 */     reader.mark(1);
/*  211 */     int ch; while ((ch = reader.read()) != -1) {
/*  212 */       if ((ch == 34) && (doubleQuotes)) {
/*  213 */         reader.mark(1);
/*  214 */         ch = reader.read();
/*  215 */         if (ch != 34) {
/*  216 */           reader.reset();
/*  217 */           if (result.length() == 0) {
/*  218 */             return null;
/*      */           }
/*  220 */           return result.toString();
/*      */         }
/*      */         
/*  223 */         result.append((char)ch);
/*  224 */       } else { if ((!doubleQuotes) && (isFieldSeparator(ch)))
/*      */         {
/*  226 */           return result.toString(); }
/*  227 */         if (((ch == 13) || (ch == 10)) && (!doubleQuotes)) {
/*  228 */           reader.reset();
/*  229 */           return result.toString();
/*      */         }
/*  231 */         result.append((char)ch);
/*  232 */         if (ch == 13)
/*      */         {
/*  234 */           reader.mark(1);
/*  235 */           ch = reader.read();
/*  236 */           if (ch == 10) {
/*  237 */             result.append((char)ch);
/*      */           } else {
/*  239 */             reader.reset();
/*      */           }
/*      */         }
/*  242 */         if ((ch == 13) || (ch == 10)) {
/*  243 */           this.lineNumber += 1;
/*      */         }
/*      */       }
/*  246 */       reader.mark(1);
/*      */     }
/*  248 */     if (doubleQuotes) {
/*  249 */       throw new ParseException("End of stream reached while parsing field.\nCurrent contact definition started at line " + this.currContactStartLineNum);
/*      */     }
/*      */     
/*  252 */     return result.toString();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private void initFields(BufferedReader reader)
/*      */     throws IOException, ContactCSV.ParseException
/*      */   {
/*  262 */     this.lineNumber = 1;
/*  263 */     this.currContactStartLineNum = 1;
/*  264 */     this.fieldNames = new ArrayList();
/*      */     
/*  266 */     if (!parseLine(reader, this.fieldNames, true)) {
/*  267 */       throw new ParseException("no column name definitions");
/*      */     }
/*      */     
/*      */ 
/*  271 */     String firstField = (String)this.fieldNames.get(0);
/*  272 */     if ((firstField != null) && (firstField.length() >= 1) && (firstField.charAt(0) == 65279)) {
/*  273 */       this.fieldNames.set(0, firstField.substring(1));
/*      */     }
/*      */     
/*  276 */     if (this.fieldNames.size() > 1000) {
/*  277 */       throw new ParseException("too many columns: " + this.fieldNames.size());
/*      */     }
/*      */     
/*      */ 
/*  281 */     for (int i = 0; i < this.fieldNames.size(); i++) {
/*  282 */       String field = (String)this.fieldNames.get(i);
/*  283 */       if (Strings.isNullOrEmpty(field)) {
/*  284 */         throw new ParseException("missing column name for column[" + i + "]");
/*      */       }
/*  286 */       if (field.length() > 100)
/*      */       {
/*  288 */         throw new ParseException(String.format("invalid format - header field %d of %d is too long (length=%d)", new Object[] { Integer.valueOf(i + 1), Integer.valueOf(this.fieldNames.size()), Integer.valueOf(field.length()) }));
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private void addMultiValueField(CsvColumn col, Map<String, String> fieldMap, ContactMap contact)
/*      */   {
/*  295 */     if (("birthday".equals(col.field)) || ("anniversary".equals(col.field))) {
/*  296 */       addMultiValueDateField(col, fieldMap, contact);
/*  297 */       return;
/*      */     }
/*  299 */     StringBuilder buf = new StringBuilder();
/*  300 */     for (String n : col.names) {
/*  301 */       String v = (String)fieldMap.get(n.toLowerCase());
/*  302 */       if (v != null) {
/*  303 */         if (buf.length() > 0) {
/*  304 */           buf.append("\n");
/*      */         }
/*  306 */         buf.append(v);
/*      */       }
/*      */     }
/*  309 */     if (buf.length() > 0) {
/*  310 */       contact.put(col.field, buf.toString());
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private String populateDateFieldsIfUnambiguous(int testY, int testM, int testD, boolean leniant)
/*      */   {
/*  323 */     if ((testY < 1600) || (testY > 4500))
/*      */     {
/*  325 */       return null;
/*      */     }
/*  327 */     int firstAllowableDay = leniant ? 1 : 13;
/*  328 */     if ((testD < firstAllowableDay) || (testD > 31))
/*      */     {
/*  330 */       return null;
/*      */     }
/*  332 */     if ((testM < 1) || (testM > 12))
/*      */     {
/*  334 */       return null;
/*      */     }
/*  336 */     ICalTimeZone tzUTC = ICalTimeZone.getUTC();
/*  337 */     GregorianCalendar cal = new GregorianCalendar(tzUTC);
/*  338 */     cal.set(testY, testM - 1, 1, 0, 0);
/*  339 */     if (testD > cal.getActualMaximum(5))
/*  340 */       return null;
/*  341 */     cal.set(5, testD);
/*  342 */     SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");
/*  343 */     ymd.setCalendar(cal);
/*  344 */     return ymd.format(cal.getTime());
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private void addDateField(String value, String field, String dateOrderKey, ContactMap contact)
/*      */   {
/*  354 */     if ((field == null) || (value == null) || (value.length() == 0)) {
/*  355 */       return;
/*      */     }
/*      */     
/*  358 */     String zimbraDateValue = null;
/*      */     try {
/*  360 */       String[] splitFields = value.split("/");
/*  361 */       if (splitFields.length != 3)
/*  362 */         splitFields = value.split("-");
/*  363 */       if (splitFields.length == 3) {
/*  364 */         int[] dateFs = new int[3];
/*  365 */         dateFs[0] = Integer.parseInt(splitFields[0]);
/*  366 */         dateFs[1] = Integer.parseInt(splitFields[1]);
/*  367 */         dateFs[2] = Integer.parseInt(splitFields[2]);
/*      */         
/*  369 */         zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[0], dateFs[1], dateFs[2], false);
/*  370 */         if (zimbraDateValue == null)
/*      */         {
/*  372 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[2], dateFs[0], dateFs[1], false);
/*      */         }
/*  374 */         if (zimbraDateValue == null)
/*      */         {
/*  376 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[2], dateFs[1], dateFs[0], false);
/*      */         }
/*  378 */         if (zimbraDateValue == null)
/*      */         {
/*  380 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[1], dateFs[2], dateFs[0], false);
/*      */         }
/*  382 */         if (zimbraDateValue == null)
/*      */         {
/*  384 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[0], dateFs[2], dateFs[1], false);
/*      */         }
/*  386 */         if (zimbraDateValue == null)
/*      */         {
/*  388 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[1], dateFs[0], dateFs[2], false);
/*      */         }
/*  390 */         if ((zimbraDateValue == null) && (dateFs[0] > 31))
/*      */         {
/*  392 */           zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[0], dateFs[1], dateFs[2], true);
/*      */         }
/*  394 */         if (zimbraDateValue == null) {
/*  395 */           String dateOrder = (String)dateOrderInfo.get(dateOrderKey);
/*  396 */           if (dateOrder != null) {
/*  397 */             int yNdx = dateOrder.indexOf('y');
/*  398 */             int mNdx = dateOrder.indexOf('m');
/*  399 */             int dNdx = dateOrder.indexOf('d');
/*  400 */             if ((yNdx != -1) && (mNdx != -1) && (dNdx != -1)) {
/*  401 */               zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[yNdx], dateFs[mNdx], dateFs[dNdx], true);
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     catch (NumberFormatException ioe) {}
/*      */     
/*  409 */     if (zimbraDateValue != null) {
/*  410 */       contact.put(field, zimbraDateValue);
/*      */ 
/*      */     }
/*  413 */     else if (Character.isDigit(value.charAt(0)))
/*      */     {
/*  415 */       contact.put(field, "'" + value + "'");
/*      */     } else {
/*  417 */       contact.put(field, value);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private void addMultiValueDateField(CsvColumn col, Map<String, String> fieldMap, ContactMap contact)
/*      */   {
/*  426 */     String zimbraDateValue = null;
/*  427 */     if (col.names.size() == 3) {
/*  428 */       int[] dateFs = new int[3];
/*  429 */       int ndx = 0;
/*      */       try {
/*  431 */         for (String n : col.names) {
/*  432 */           String v = (String)fieldMap.get(n.toLowerCase());
/*  433 */           if (v != null) {
/*  434 */             dateFs[ndx] = Integer.parseInt(v);
/*      */           }
/*  436 */           ndx++;
/*      */         }
/*  438 */         zimbraDateValue = populateDateFieldsIfUnambiguous(dateFs[2], dateFs[0], dateFs[1], true);
/*      */       }
/*      */       catch (NumberFormatException ioe) {}
/*      */     }
/*  442 */     if (zimbraDateValue != null) {
/*  443 */       contact.put(col.field, zimbraDateValue);
/*      */     } else {
/*  445 */       StringBuilder buf = new StringBuilder();
/*  446 */       for (String n : col.names) {
/*  447 */         String v = (String)fieldMap.get(n.toLowerCase());
/*  448 */         if (v != null) {
/*  449 */           if (buf.length() > 0) {
/*  450 */             buf.append("-");
/*      */           }
/*  452 */           buf.append(v);
/*      */         }
/*      */       }
/*  455 */       if (buf.length() == 0) {
/*  456 */         return;
/*      */       }
/*  458 */       zimbraDateValue = buf.toString();
/*      */       
/*  460 */       if (Character.isDigit(zimbraDateValue.charAt(0)))
/*      */       {
/*  462 */         contact.put(col.field, "'" + zimbraDateValue + "'");
/*      */       } else {
/*  464 */         contact.put(col.field, zimbraDateValue);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private void addNameField(String value, String field, ContactMap contact) {
/*  470 */     if ((field == null) || (value == null))
/*  471 */       return;
/*  472 */     String[] nameFields = field.split(",");
/*  473 */     String firstNameField = nameFields[0];
/*      */     
/*  475 */     if (firstNameField == null) {
/*  476 */       return;
/*      */     }
/*  478 */     String middleNameField = null;String lastNameField = null;
/*  479 */     if (nameFields.length == 2) {
/*  480 */       lastNameField = nameFields[1];
/*  481 */     } else if (nameFields.length == 3) {
/*  482 */       middleNameField = nameFields[1];
/*  483 */       lastNameField = nameFields[2];
/*      */     }
/*      */     else {
/*  486 */       contact.put(firstNameField, value);
/*  487 */       return;
/*      */     }
/*      */     
/*  490 */     int comma = value.indexOf(this.fieldSeparator);
/*  491 */     if (comma > 0)
/*      */     {
/*  493 */       contact.put(lastNameField, value.substring(0, comma).trim());
/*  494 */       contact.put(firstNameField, value.substring(comma + 1).trim());
/*  495 */       return;
/*      */     }
/*      */     
/*  498 */     int space = value.indexOf(' ');
/*  499 */     if (space == -1) {
/*  500 */       contact.put(firstNameField, value);
/*  501 */       return;
/*      */     }
/*  503 */     contact.put(firstNameField, value.substring(0, space).trim());
/*  504 */     space++;
/*  505 */     if (middleNameField != null) {
/*  506 */       int anotherSpace = value.indexOf(' ', space);
/*  507 */       if (anotherSpace > 0) {
/*  508 */         contact.put(middleNameField, value.substring(space, anotherSpace).trim());
/*  509 */         space = anotherSpace + 1;
/*      */       }
/*      */     }
/*  512 */     if (space < value.length()) {
/*  513 */       contact.put(lastNameField, value.substring(space).trim());
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   public static String getTags(Map<String, String> csv)
/*      */   {
/*  520 */     return (String)csv.remove("__tag");
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private Map<String, String> toContact(List<String> csv, CsvFormat format)
/*      */     throws ContactCSV.ParseException
/*      */   {
/*  531 */     ContactMap contactMap = new ContactMap();
/*      */     
/*      */ 
/*      */ 
/*  535 */     if (csv == null) {
/*  536 */       return contactMap.getContacts();
/*      */     }
/*  538 */     if (format.allFields()) {
/*  539 */       int end = csv.size();
/*  540 */       end = end > this.fieldNames.size() ? this.fieldNames.size() : end;
/*  541 */       for (int i = 0; i < end; i++) {
/*  542 */         contactMap.put((String)this.fieldNames.get(i), (String)csv.get(i));
/*      */       }
/*  544 */     } else if (format.hasNoHeaders()) {
/*  545 */       int end = csv.size();
/*  546 */       end = end > format.columns.size() ? format.columns.size() : end;
/*  547 */       for (int i = 0; i < end; i++) {
/*  548 */         contactMap.put(((CsvColumn)format.columns.get(i)).field, (String)csv.get(i));
/*      */       }
/*      */       
/*      */ 
/*      */     }
/*      */     else
/*      */     {
/*  555 */       Map<CsvColumn, Map<String, String>> pendMV = new HashMap();
/*  556 */       List<CsvColumn> unseenColumns = new ArrayList();
/*  557 */       unseenColumns.addAll(format.columns);
/*  558 */       for (int ndx = 0; ndx < this.fieldNames.size(); ndx++) {
/*  559 */         String csvFieldName = (String)this.fieldNames.get(ndx);
/*  560 */         String fieldValue = ndx >= csv.size() ? null : (String)csv.get(ndx);
/*  561 */         CsvColumn matchingCol = null;
/*  562 */         String matchingFieldLc = null;
/*  563 */         for (CsvColumn unseenC : unseenColumns) {
/*  564 */           matchingFieldLc = unseenC.matchingLcCsvFieldName(csvFieldName);
/*  565 */           if (matchingFieldLc != null)
/*      */           {
/*      */ 
/*  568 */             if (unseenC.colType == ColType.MULTIVALUE) {
/*  569 */               Map<String, String> currMV = (Map)pendMV.get(matchingCol);
/*  570 */               if ((currMV != null) && (currMV.get(matchingFieldLc) != null)) {
/*      */                 break;
/*      */               }
/*      */             }
/*      */             else {
/*  575 */               matchingCol = unseenC;
/*      */             } }
/*      */         }
/*  578 */         if (matchingCol == null)
/*      */         {
/*  580 */           LOG.debug("Adding CSV contact attribute [%s=%s] - assuming is user defined.", new Object[] { csvFieldName, fieldValue });
/*  581 */           contactMap.put(csvFieldName, fieldValue);
/*      */         }
/*      */         else {
/*  584 */           switch (matchingCol.colType) {
/*      */           case NAME: 
/*  586 */             addNameField(fieldValue, matchingCol.field, contactMap);
/*  587 */             unseenColumns.remove(matchingCol);
/*  588 */             break;
/*      */           case DATE: 
/*  590 */             addDateField(fieldValue, matchingCol.field, format.key(), contactMap);
/*  591 */             unseenColumns.remove(matchingCol);
/*  592 */             break;
/*      */           case TAG: 
/*  594 */             contactMap.put("__tag", fieldValue);
/*  595 */             break;
/*      */           case MULTIVALUE: 
/*  597 */             for (String cname : matchingCol.names) {
/*  598 */               if (cname.toLowerCase().equals(matchingFieldLc)) {
/*  599 */                 Map<String, String> currMV = (Map)pendMV.get(matchingCol);
/*  600 */                 if (currMV == null) {
/*  601 */                   currMV = new HashMap();
/*  602 */                   pendMV.put(matchingCol, currMV);
/*      */                 }
/*  604 */                 currMV.put(matchingFieldLc, fieldValue);
/*  605 */                 if (currMV.size() >= matchingCol.names.size()) {
/*  606 */                   addMultiValueField(matchingCol, currMV, contactMap);
/*  607 */                   pendMV.remove(currMV);
/*  608 */                   unseenColumns.remove(matchingCol);
/*      */                 }
/*      */               }
/*      */             }
/*  612 */             break;
/*      */           default: 
/*  614 */             contactMap.put(matchingCol.field, fieldValue);
/*  615 */             unseenColumns.remove(matchingCol);
/*      */           }
/*      */         }
/*      */       }
/*  619 */       for (Map.Entry<CsvColumn, Map<String, String>> entry : pendMV.entrySet()) {
/*  620 */         addMultiValueField((CsvColumn)entry.getKey(), (Map)entry.getValue(), contactMap);
/*      */       }
/*      */     }
/*      */     
/*  624 */     Map<String, String> contact = contactMap.getContacts();
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  633 */     if (contact.size() == 1) {
/*  634 */       boolean onlyBlank = true;
/*  635 */       for (String val : contact.values()) {
/*  636 */         if (!val.trim().equals("")) {
/*  637 */           onlyBlank = false;
/*  638 */           break;
/*      */         }
/*      */       }
/*  641 */       if (onlyBlank)
/*  642 */         contact = new HashMap();
/*      */     }
/*  644 */     return contact;
/*      */   }
/*      */   
/*      */   private List<Map<String, String>> getContactsInternal(BufferedReader reader, String fmt, String locale) throws ContactCSV.ParseException {
/*      */     try {
/*  649 */       CsvFormat format = null;
/*  650 */       initFields(reader);
/*      */       
/*  652 */       if (fmt == null) {
/*  653 */         format = guessFormat(this.fieldNames);
/*      */       } else {
/*  655 */         format = getFormat(fmt, locale);
/*      */       }
/*      */       
/*  658 */       LOG.debug("getContactsInternal requested format/locale=[%s/%s]: using %s", new Object[] { fmt, locale, format });
/*  659 */       List<Map<String, String>> result = new ArrayList();
/*  660 */       List<String> fields = new ArrayList();
/*      */       
/*  662 */       while (parseLine(reader, fields, false)) {
/*  663 */         Map<String, String> contact = toContact(fields, format);
/*  664 */         if (contact.size() > 0)
/*  665 */           result.add(contact);
/*      */       }
/*  667 */       return result;
/*      */     } catch (IOException e) {
/*  669 */       LOG.debug("Encountered IOException", e);
/*  670 */       throw new ParseException(e.getMessage() + " at line number " + this.lineNumber, e);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   public static List<Map<String, String>> getContacts(BufferedReader reader, String fmt, String locale)
/*      */     throws ContactCSV.ParseException
/*      */   {
/*  678 */     ContactCSV csv = new ContactCSV();
/*  679 */     return csv.getContactsInternal(reader, fmt, locale);
/*      */   }
/*      */   
/*      */   public static List<Map<String, String>> getContacts(BufferedReader reader, String fmt) throws ContactCSV.ParseException {
/*  683 */     return getContacts(reader, fmt, null);
/*      */   }
/*      */   
/*      */   public static class ParseException extends Exception
/*      */   {
/*      */     ParseException(String msg) {
/*  689 */       super();
/*      */     }
/*      */     
/*      */     ParseException(String msg, Throwable cause) {
/*  693 */       super(cause);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private static void populateDelimiterInfo(org.dom4j.Element delimiters)
/*      */   {
/*  702 */     delimiterInfo = new HashMap();
/*      */     
/*  704 */     for (Iterator elements = delimiters.elementIterator(DELIMITER); elements.hasNext();) {
/*  705 */       org.dom4j.Element field = (org.dom4j.Element)elements.next();
/*  706 */       String delim = field.attributeValue("char");
/*  707 */       if ((delim != null) && (!delim.isEmpty()))
/*      */       {
/*      */ 
/*  710 */         String format = field.attributeValue("format");
/*  711 */         if ((format != null) && (!format.isEmpty()))
/*      */         {
/*      */ 
/*  714 */           String myLocale = field.attributeValue("locale");
/*  715 */           if ((myLocale != null) && (!myLocale.isEmpty())) {
/*  716 */             format = format + "/" + myLocale;
/*      */           }
/*  718 */           delimiterInfo.put(format, Character.valueOf(delim.charAt(0)));
/*      */         }
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   private static void populateDateFormatInfo(org.dom4j.Element dateFormats)
/*      */   {
/*  727 */     dateOrderInfo = new HashMap();
/*      */     
/*  729 */     for (Iterator elements = dateFormats.elementIterator(DATEFORMAT); elements.hasNext();) {
/*  730 */       org.dom4j.Element dateFormat = (org.dom4j.Element)elements.next();
/*  731 */       String origOrder = dateFormat.attributeValue("order");
/*  732 */       if ((origOrder != null) && (!origOrder.isEmpty()))
/*      */       {
/*      */ 
/*  735 */         String order = origOrder.toLowerCase();
/*  736 */         if ((!order.equals("ymd")) && (!order.equals("ydm")) && (!order.equals("myd")) && (!order.equals("mdy")) && (!order.equals("dmy")) && (!order.equals("dym")))
/*      */         {
/*  738 */           LOG.debug("invalid \"order\" %s in zimbra-contact-fields.xml", new Object[] { origOrder });
/*      */         }
/*      */         else {
/*  741 */           String format = dateFormat.attributeValue("format");
/*  742 */           if ((format != null) && (!format.isEmpty()))
/*      */           {
/*      */ 
/*  745 */             String myLocale = dateFormat.attributeValue("locale");
/*  746 */             if ((myLocale != null) && (!myLocale.isEmpty())) {
/*  747 */               format = format + "/" + myLocale;
/*      */             }
/*  749 */             dateOrderInfo.put(format, order);
/*      */           }
/*      */         }
/*      */       } } }
/*      */   
/*  754 */   private static void populateFields(org.dom4j.Element fields) { knownFields = new HashSet();
/*      */     
/*  756 */     for (Iterator elements = fields.elementIterator(FIELD); elements.hasNext();) {
/*  757 */       org.dom4j.Element field = (org.dom4j.Element)elements.next();
/*  758 */       knownFields.add(field.attributeValue("name"));
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   private static class ContactMap
/*      */   {
/*      */     private Map<String, String> contacts;
/*      */     
/*      */     private Set<String> seenFields;
/*      */     
/*      */ 
/*      */     public ContactMap()
/*      */     {
/*  772 */       this.contacts = new HashMap();
/*  773 */       this.seenFields = new HashSet();
/*      */     }
/*      */     
/*      */     public boolean put(String key, String value) {
/*  777 */       if ((key == null) || (value == null))
/*  778 */         return false;
/*  779 */       if (key.trim().equals(""))
/*  780 */         return false;
/*  781 */       if (value.length() <= 0)
/*  782 */         return false;
/*  783 */       String lkey = key.toLowerCase();
/*  784 */       if (this.seenFields.contains(lkey))
/*  785 */         return false;
/*  786 */       this.seenFields.add(lkey);
/*      */       
/*  788 */       this.contacts.put(key, value);
/*  789 */       return true;
/*      */     }
/*      */     
/*      */ 
/*  793 */     public Map<String, String> getContacts() { return this.contacts; }
/*      */   }
/*      */   
/*      */   private static class CsvColumn {
/*      */     String name;
/*      */     String field;
/*      */     List<String> names;
/*      */     ContactCSV.ColType colType;
/*      */     
/*      */     CsvColumn(org.dom4j.Element col) {
/*  803 */       this.names = new ArrayList();
/*  804 */       this.name = col.attributeValue("name");
/*  805 */       this.field = col.attributeValue("field");
/*  806 */       this.colType = ContactCSV.ColType.SIMPLE;
/*  807 */       String type = col.attributeValue("type");
/*  808 */       if (type == null)
/*  809 */         return;
/*  810 */       if (type.equals("multivalue")) {
/*  811 */         this.colType = ContactCSV.ColType.MULTIVALUE;
/*  812 */         Collections.addAll(this.names, this.name.split(","));
/*  813 */         this.name = ((String)this.names.get(0));
/*  814 */       } else if (type.equals("name")) {
/*  815 */         this.colType = ContactCSV.ColType.NAME;
/*  816 */       } else if (type.equals("tag")) {
/*  817 */         this.colType = ContactCSV.ColType.TAG;
/*  818 */       } else if (type.equals("date")) {
/*  819 */         this.colType = ContactCSV.ColType.DATE;
/*      */       }
/*      */     }
/*      */     
/*      */     public String toString()
/*      */     {
/*  825 */       Objects.ToStringHelper helper = Objects.toStringHelper(this).add("name", this.name).add("field", this.field).add("type", this.colType);
/*      */       
/*  827 */       if (this.colType == ContactCSV.ColType.MULTIVALUE) {
/*  828 */         helper.add("cols", this.names);
/*      */       }
/*  830 */       return helper.toString();
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     public String matchingLcCsvFieldName(String fieldName)
/*      */     {
/*  840 */       String lcFieldName = fieldName.toLowerCase();
/*  841 */       if (this.colType == ContactCSV.ColType.MULTIVALUE) {
/*  842 */         for (String colName : this.names) {
/*  843 */           if (colName.toLowerCase().equals(lcFieldName)) {
/*  844 */             return lcFieldName;
/*      */           }
/*      */           
/*      */         }
/*  848 */       } else if (this.name.toLowerCase().equals(lcFieldName)) {
/*  849 */         return lcFieldName;
/*      */       }
/*  851 */       return null;
/*      */     }
/*      */   }
/*      */   
/*      */   private static class CsvFormat implements Comparable<CsvFormat> {
/*      */     String name;
/*      */     String locale;
/*      */     Set<String> flags;
/*      */     List<ContactCSV.CsvColumn> columns;
/*      */     Map<String, String> forwardMapping;
/*      */     Map<String, String> reverseMapping;
/*      */     
/*      */     CsvFormat(org.dom4j.Element fmt) {
/*  864 */       this.name = fmt.attributeValue("name");
/*  865 */       this.locale = fmt.attributeValue("locale");
/*  866 */       String f = fmt.attributeValue("flag");
/*  867 */       this.flags = new HashSet();
/*  868 */       if (f != null)
/*  869 */         Collections.addAll(this.flags, f.toLowerCase().split(","));
/*  870 */       this.columns = new ArrayList();
/*  871 */       this.forwardMapping = new HashMap();
/*  872 */       this.reverseMapping = new HashMap();
/*      */     }
/*      */     
/*  875 */     void add(org.dom4j.Element col) { ContactCSV.CsvColumn newColumn = new ContactCSV.CsvColumn(col);
/*  876 */       this.columns.add(newColumn);
/*  877 */       this.forwardMapping.put(newColumn.name, newColumn.field);
/*  878 */       this.reverseMapping.put(newColumn.field, newColumn.name);
/*      */     }
/*      */     
/*  881 */     boolean hasFlag(String f) { return this.flags.contains(f.toLowerCase()); }
/*      */     
/*      */     boolean hasNoHeaders() {
/*  884 */       return hasFlag("noheader");
/*      */     }
/*      */     
/*  887 */     boolean allFields() { return hasFlag("allfields"); }
/*      */     
/*      */ 
/*      */     public String toString()
/*      */     {
/*  892 */       return Objects.toStringHelper(this).add("name", this.name).add("locale", this.locale).add("flags", this.flags).toString();
/*      */     }
/*      */     
/*      */     public String key() {
/*  896 */       String myKey = this.name;
/*  897 */       if (this.locale != null) {
/*  898 */         myKey = myKey + "/" + this.locale;
/*      */       }
/*  900 */       return myKey;
/*      */     }
/*      */     
/*      */     public int compareTo(CsvFormat o)
/*      */     {
/*  905 */       int BEFORE = -1;
/*  906 */       int EQUAL = 0;
/*  907 */       int AFTER = 1;
/*      */       
/*  909 */       if (this == o) { return 0;
/*      */       }
/*  911 */       int nameSame = this.name.compareTo(o.name);
/*  912 */       if (nameSame != 0)
/*  913 */         return nameSame;
/*  914 */       if (this.locale == null) {
/*  915 */         if (o.locale == null)
/*  916 */           return nameSame;
/*  917 */         return 1;
/*      */       }
/*  919 */       if (o.locale == null)
/*  920 */         return -1;
/*  921 */       return this.locale.compareTo(o.locale);
/*      */     }
/*      */   }
/*      */   
/*      */   private static void addFormat(org.dom4j.Element format)
/*      */   {
/*  927 */     CsvFormat fmt = new CsvFormat(format);
/*      */     
/*  929 */     for (Iterator elements = format.elementIterator(COLUMN); elements.hasNext();) {
/*  930 */       org.dom4j.Element col = (org.dom4j.Element)elements.next();
/*  931 */       fmt.add(col);
/*      */     }
/*      */     
/*  934 */     if (knownFormats == null) {
/*  935 */       knownFormats = new HashSet();
/*      */     }
/*  937 */     knownFormats.add(fmt);
/*  938 */     if (fmt.hasFlag("default")) {
/*  939 */       defaultFormat = fmt;
/*      */     }
/*      */   }
/*      */   
/*      */   static {
/*      */     try {
/*  945 */       readMappingFile(LC.zimbra_csv_mapping_file.value());
/*      */     } catch (Exception e) {
/*  947 */       LOG.error("error initializing csv mapping file", e);
/*      */     }
/*      */   }
/*      */   
/*      */   private static void readMappingFile(String mappingFile) throws IOException, DocumentException {
/*  952 */     readMapping(new FileInputStream(mappingFile));
/*      */   }
/*      */   
/*      */   private static void readMapping(InputStream is) throws DocumentException {
/*  956 */     delimiterInfo = new HashMap();
/*  957 */     dateOrderInfo = new HashMap();
/*  958 */     org.dom4j.Element root = com.zimbra.common.soap.Element.getSAXReader().read(is).getRootElement();
/*  959 */     for (Iterator elements = root.elementIterator(); elements.hasNext();) {
/*  960 */       org.dom4j.Element elem = (org.dom4j.Element)elements.next();
/*  961 */       if (elem.getQName().equals(FIELDS)) {
/*  962 */         populateFields(elem);
/*  963 */       } else if (elem.getQName().equals(FORMAT)) {
/*  964 */         addFormat(elem);
/*  965 */       } else if (elem.getQName().equals(DELIMITERS)) {
/*  966 */         populateDelimiterInfo(elem);
/*  967 */       } else if (elem.getQName().equals(DATEFORMATS)) {
/*  968 */         populateDateFormatInfo(elem);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private static CsvFormat guessFormat(List<String> keys) throws ContactCSV.ParseException {
/*  974 */     if ((knownFormats == null) || (defaultFormat == null)) {
/*  975 */       throw new ParseException("missing config file " + LC.zimbra_csv_mapping_file.value());
/*      */     }
/*      */     
/*      */ 
/*  979 */     int numBestMatch = 0;
/*  980 */     CsvFormat bestMatch = defaultFormat;
/*      */     
/*  982 */     for (CsvFormat f : knownFormats) {
/*  983 */       int numMatchedFields = 0;
/*  984 */       for (CsvColumn col : f.columns) {
/*  985 */         if (keys.contains(col.name)) {
/*  986 */           numMatchedFields++;
/*      */         }
/*      */       }
/*  989 */       if (numMatchedFields > numBestMatch) {
/*  990 */         numBestMatch = numMatchedFields;
/*  991 */         bestMatch = f;
/*      */       }
/*      */     }
/*  994 */     LOG.debug("Best matching format='%s'", new Object[] { bestMatch });
/*  995 */     return bestMatch;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private static CsvFormat getFormat(String fmt, String locale)
/*      */     throws ContactCSV.ParseException
/*      */   {
/* 1006 */     if ((knownFormats == null) || (defaultFormat == null)) {
/* 1007 */       throw new ParseException("missing config file " + LC.zimbra_csv_mapping_file.value());
/*      */     }
/*      */     
/* 1010 */     if (locale != null) {
/* 1011 */       for (CsvFormat f : knownFormats) {
/* 1012 */         if ((f.locale != null) && (f.name.equals(fmt)) && (f.locale.equals(locale))) {
/* 1013 */           return f;
/*      */         }
/*      */       }
/*      */     }
/* 1017 */     for (CsvFormat f : knownFormats) {
/* 1018 */       if ((f.name.equals(fmt)) && (f.locale == null)) {
/* 1019 */         return f;
/*      */       }
/*      */     }
/* 1022 */     return defaultFormat;
/*      */   }
/*      */   
/*      */   public void toCSV(String format, String locale, Character separator, Iterator<? extends MailItem> contacts, StringBuilder sb) throws ContactCSV.ParseException, ServiceException
/*      */   {
/* 1027 */     if (knownFormats == null) {
/* 1028 */       return;
/*      */     }
/*      */     
/* 1031 */     CsvFormat fmt = getFormat(format, locale);
/* 1032 */     if (separator != null) {
/* 1033 */       this.fieldSeparator = separator.charValue();
/*      */     } else {
/* 1035 */       String delimKey = fmt.key();
/* 1036 */       Character formatDefaultDelim = (Character)delimiterInfo.get(delimKey);
/* 1037 */       if (formatDefaultDelim != null) {
/* 1038 */         LOG.debug("toCSV choosing %c from <delimiter> matching %s", new Object[] { formatDefaultDelim, delimKey });
/* 1039 */         this.fieldSeparator = formatDefaultDelim.charValue();
/*      */       }
/*      */     }
/* 1042 */     LOG.debug("toCSV Requested=[format=\"%s\" locale=\"%s\" delim=\"%c\"] Actual=[%s delim=\"%c\"]", new Object[] { format, locale, separator, fmt, Character.valueOf(this.fieldSeparator) });
/*      */     
/* 1044 */     if (fmt == null) {
/* 1045 */       return;
/*      */     }
/* 1047 */     if (fmt.allFields()) {
/* 1048 */       ArrayList<Map<String, String>> allContacts = new ArrayList();
/* 1049 */       HashSet<String> fields = new HashSet();
/* 1050 */       while (contacts.hasNext()) {
/* 1051 */         Object obj = contacts.next();
/* 1052 */         if ((obj instanceof Contact)) {
/* 1053 */           Contact c = (Contact)obj;
/*      */           
/* 1055 */           if (c.isContactGroup())
/*      */           {
/* 1057 */             HashMap<String, String> nContacts = new HashMap();
/*      */             
/* 1059 */             nContacts.putAll(c.getFields());
/*      */             
/* 1061 */             nContacts.remove(ContactConstants.A_groupMember);
/*      */             
/* 1063 */             ContactGroup cg = ContactGroup.init(c, false);
/* 1064 */             String strs = cg.migrateToDlist(this.mbox, this.octxt);
/* 1065 */             nContacts.put(ContactConstants.A_dlist, strs);
/* 1066 */             allContacts.add(nContacts);
/* 1067 */             fields.addAll(nContacts.keySet());
/*      */           }
/*      */           else
/*      */           {
/* 1071 */             allContacts.add(c.getFields());
/* 1072 */             fields.addAll(c.getFields().keySet());
/*      */           }
/*      */         }
/*      */       }
/* 1076 */       ArrayList<String> allFields = new ArrayList();
/* 1077 */       allFields.addAll(fields);
/* 1078 */       Collections.sort(allFields);
/* 1079 */       addFieldDef(allFields, sb);
/* 1080 */       for (Map<String, String> contactMap : allContacts) {
/* 1081 */         toCSVContact(allFields, contactMap, sb);
/*      */       }
/* 1083 */       return;
/*      */     }
/*      */     
/* 1086 */     if (!fmt.hasNoHeaders()) {
/* 1087 */       addFieldDef(fmt, sb);
/*      */     }
/* 1089 */     while (contacts.hasNext()) {
/* 1090 */       Object c = contacts.next();
/* 1091 */       if ((c instanceof Contact)) {
/* 1092 */         toCSVContact(fmt, (Contact)c, sb);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private static void addFieldValue(Map<String, String> contact, String name, String field, StringBuilder sb) {
/* 1098 */     String value = (String)contact.get(field == null ? name : field);
/* 1099 */     if (value == null) {
/* 1100 */       value = "";
/*      */     }
/* 1102 */     sb.append('"').append(value.replaceAll("\"", "\"\"")).append('"');
/*      */   }
/*      */   
/*      */   private void toCSVContact(List<String> fields, Map<String, String> contact, StringBuilder sb) {
/* 1106 */     boolean first = true;
/* 1107 */     for (String f : fields) {
/* 1108 */       if (!first) {
/* 1109 */         sb.append(this.fieldSeparator);
/*      */       }
/* 1111 */       addFieldValue(contact, f, f, sb);
/* 1112 */       first = false;
/*      */     }
/* 1114 */     sb.append("\r\n");
/*      */   }
/*      */   
/*      */   private void toCSVContact(CsvFormat fmt, Contact c, StringBuilder sb) {
/* 1118 */     boolean first = true;
/* 1119 */     for (CsvColumn col : fmt.columns) {
/* 1120 */       if (!first) {
/* 1121 */         sb.append(this.fieldSeparator);
/*      */       }
/* 1123 */       if (col.colType == ColType.TAG) {
/* 1124 */         boolean firstTag = true;
/* 1125 */         sb.append('"');
/* 1126 */         for (String tname : c.getTags()) {
/* 1127 */           if (!firstTag) {
/* 1128 */             sb.append(this.fieldSeparator);
/*      */           }
/* 1130 */           sb.append(tname.replace("\\", "\\\\").replace("\"", "\\\""));
/* 1131 */           firstTag = false;
/*      */         }
/* 1133 */         sb.append('"');
/*      */       } else {
/* 1135 */         addFieldValue(c.getFields(), col.name, col.field, sb);
/*      */       }
/* 1137 */       first = false;
/*      */     }
/* 1139 */     sb.append("\r\n");
/*      */   }
/*      */   
/*      */   private void addFieldDef(List<String> fields, StringBuilder sb) {
/* 1143 */     boolean first = true;
/* 1144 */     for (String f : fields) {
/* 1145 */       if (!first) {
/* 1146 */         sb.append(this.fieldSeparator);
/*      */       }
/* 1148 */       sb.append('"').append(f).append('"');
/* 1149 */       first = false;
/*      */     }
/* 1151 */     sb.append("\r\n");
/*      */   }
/*      */   
/*      */   private void addFieldDef(CsvFormat fmt, StringBuilder sb) {
/* 1155 */     boolean first = true;
/* 1156 */     for (CsvColumn col : fmt.columns) {
/* 1157 */       if (!first) {
/* 1158 */         sb.append(this.fieldSeparator);
/*      */       }
/* 1160 */       sb.append('"').append(col.name).append('"');
/* 1161 */       first = false;
/*      */     }
/* 1163 */     sb.append("\r\n");
/*      */   }
/*      */   
/*      */   private static void writeLine(OutputStream out, String line) throws IOException {
/* 1167 */     out.write(line.getBytes());
/* 1168 */     out.write(10);
/*      */   }
/*      */   
/*      */   private static void dump(OutputStream out) throws IOException {
/* 1172 */     writeLine(out, "=== Fields ===");
/* 1173 */     for (String f : new TreeSet(knownFields)) {
/* 1174 */       writeLine(out, f);
/*      */     }
/* 1176 */     for (Iterator i$ = new TreeSet(knownFormats).iterator(); i$.hasNext();) { fmt = (CsvFormat)i$.next();
/* 1177 */       StringBuilder sb = new StringBuilder("=== Mapping ");
/* 1178 */       sb.append(fmt.toString()).append(" ===");
/* 1179 */       writeLine(out, sb.toString());
/* 1180 */       for (CsvColumn col : fmt.columns) {
/* 1181 */         if ((col.field != null) && 
/* 1182 */           (!knownFields.contains(col.field))) {
/* 1183 */           LOG.debug("Mapping '%s' references unknown field='%s'\n", new Object[] { fmt.name, col.field });
/*      */         }
/*      */         
/* 1186 */         writeLine(out, col.toString());
/*      */       }
/*      */     }
/*      */     CsvFormat fmt;
/*      */   }
/*      */   
/* 1192 */   public static String[] getAllFormatNames() { Set<String> formats = new HashSet();
/* 1193 */     for (CsvFormat f : knownFormats) {
/* 1194 */       formats.add(f.name);
/*      */     }
/* 1196 */     return (String[])formats.toArray(new String[0]);
/*      */   }
/*      */   
/*      */   public static void main(String[] args) throws IOException, DocumentException {
/* 1200 */     ZimbraLog.toolSetupLog4jConsole("INFO", true, false);
/*      */     
/* 1202 */     if (args.length > 0) {
/* 1203 */       knownFormats = new HashSet();
/* 1204 */       readMappingFile(args[0]);
/*      */     }
/* 1206 */     dump(System.out);
/* 1207 */     writeLine(System.out, "");
/* 1208 */     System.out.print("All Format Names:");
/* 1209 */     for (String fmtName : getAllFormatNames()) {
/* 1210 */       System.out.print(" " + fmtName);
/*      */     }
/* 1212 */     System.out.println();
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/service/formatter/ContactCSV.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */